﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;


public class FM_SoundPool
{
    public static string TAG = Constants.TAG;
    public static FM_TimeSignatureValue TIME_SIGNATURE_N;
    public static FM_TimeSignatureValue TIME_SIGNATURE_D;
    public static int FALLBACK_DURATION = 250; // fallback duration for sounds
    public static int MAX_TRACKS = 500;

    private readonly List<FM_AudioTrack> Tracks = new List<FM_AudioTrack>();

    private void UpdateTrackList(int index)
    {
        Tracks[index].AccessIndex = -1;
        foreach (FM_AudioTrack t in Tracks)
        {
            t.AccessIndex += 1;
        }
        for (int i = Tracks.Count - 1; i >= 0; i--)
        {
            if (Tracks[i].AccessIndex > MAX_TRACKS)
                Tracks.RemoveAt(i);
        }
    }

    public void ClearAudioTracks()
    {
        Tracks.Clear();
    }

    public FM_AudioTrack CreateTrack(List<int> tracks, List<int> d)
    {
        FM_AudioSubTrack[] track = new FM_AudioSubTrack[7];
        for (int i = 0; i < tracks.Count; i++)
        {
            if (tracks[i] != -1)
                track[i] = new FM_AudioSubTrack(tracks[i], d[i]);
        }
        return CheckAndCreate(track);
    }

    private FM_AudioTrack CheckAndCreate(FM_AudioSubTrack[] track)
    {
        FM_AudioTrack t;
        for (int i = 0; i < Tracks.Count; i++)
        {
            if (Tracks[i].Check(track[0], track[1], track[2], track[3], track[4], track[5], track[6]))
            {
                t = Tracks[i];
                UpdateTrackList(i);
                return t;
            }
        }
        t = new FM_AudioTrack(track[0], track[1], track[2], track[3], track[4], track[5], track[6]);
        Tracks.Add(t);
        UpdateTrackList(Tracks.Count - 1);
        return t;
    }

    // Hold the playing threads
    private readonly Dictionary<int,PlayThread> threadMap;
    public static volatile bool playing;

    // Hold the audio files
    public static  Dictionary<int, string> assetFiles = new Dictionary<int,string>();
    protected static Dictionary<string, int> KeyMapping = new Dictionary<string, int>();

    // Hold the loaded audio files
    public static Dictionary<int, AudioClip> soundMap = new Dictionary<int,AudioClip>();

    public static void InitMap()
    {
        assetFiles = new Dictionary<int, string>();
        KeyMapping = new Dictionary<string, int>();

        assetFiles.Add(1, "sounds/_0a.wav");
        assetFiles.Add(2, "sounds/_0b.wav");
        assetFiles.Add(3, "sounds/_0ad.wav");
        assetFiles.Add(4, "sounds/_1c.wav");
        assetFiles.Add(5, "sounds/_1d.wav");
        assetFiles.Add(6, "sounds/_1cd.wav");
        assetFiles.Add(7, "sounds/_1e.wav");
        assetFiles.Add(8, "sounds/_1dd.wav");
        assetFiles.Add(9, "sounds/_1f.wav");
        assetFiles.Add(10, "sounds/_1g.wav");
        assetFiles.Add(11, "sounds/_1fd.wav");
        assetFiles.Add(12, "sounds/_1a.wav");
        assetFiles.Add(13, "sounds/_1gd.wav");
        assetFiles.Add(14, "sounds/_1b.wav");
        assetFiles.Add(15, "sounds/_1ad.wav");
        assetFiles.Add(16, "sounds/_2c.wav");
        assetFiles.Add(17, "sounds/_2d.wav");
        assetFiles.Add(18, "sounds/_2cd.wav");
        assetFiles.Add(19, "sounds/_2e.wav");
        assetFiles.Add(20, "sounds/_2dd.wav");
        assetFiles.Add(21, "sounds/_2f.wav");
        assetFiles.Add(22, "sounds/_2g.wav");
        assetFiles.Add(23, "sounds/_2fd.wav");
        assetFiles.Add(24, "sounds/_2a.wav");
        assetFiles.Add(25, "sounds/_2gd.wav");
        assetFiles.Add(26, "sounds/_2b.wav");
        assetFiles.Add(27, "sounds/_2ad.wav");
        assetFiles.Add(28, "sounds/_3c.wav");
        assetFiles.Add(29, "sounds/_3d.wav");
        assetFiles.Add(30, "sounds/_3cd.wav");
        assetFiles.Add(31, "sounds/_3e.wav");
        assetFiles.Add(32, "sounds/_3dd.wav");
        assetFiles.Add(33, "sounds/_3f.wav");
        assetFiles.Add(34, "sounds/_3g.wav");
        assetFiles.Add(35, "sounds/_3fd.wav");
        assetFiles.Add(36, "sounds/_3a.wav");
        assetFiles.Add(37, "sounds/_3gd.wav");
        assetFiles.Add(38, "sounds/_3b.wav");
        assetFiles.Add(39, "sounds/_3ad.wav");
        assetFiles.Add(40, "sounds/_4c.wav");
        assetFiles.Add(41, "sounds/_4d.wav");
        assetFiles.Add(42, "sounds/_4cd.wav");
        assetFiles.Add(43, "sounds/_4e.wav");
        assetFiles.Add(44, "sounds/_4dd.wav");
        assetFiles.Add(45, "sounds/_4f.wav");
        assetFiles.Add(46, "sounds/_4g.wav");
        assetFiles.Add(47, "sounds/_4fd.wav");
        assetFiles.Add(48, "sounds/_4a.wav");
        assetFiles.Add(49, "sounds/_4gd.wav");
        assetFiles.Add(50, "sounds/_4b.wav");
        assetFiles.Add(51, "sounds/_4ad.wav");
        assetFiles.Add(52, "sounds/_5c.wav");
        assetFiles.Add(53, "sounds/_5d.wav");
        assetFiles.Add(54, "sounds/_5cd.wav");
        assetFiles.Add(55, "sounds/_5e.wav");
        assetFiles.Add(56, "sounds/_5dd.wav");
        assetFiles.Add(57, "sounds/_5f.wav");
        assetFiles.Add(58, "sounds/_5g.wav");
        assetFiles.Add(59, "sounds/_5fd.wav");
        assetFiles.Add(60, "sounds/_5a.wav");
        assetFiles.Add(61, "sounds/_5gd.wav");
        assetFiles.Add(62, "sounds/_5b.wav");
        assetFiles.Add(63, "sounds/_5ad.wav");
        assetFiles.Add(64, "sounds/_6c.wav");
        assetFiles.Add(65, "sounds/_6d.wav");
        assetFiles.Add(66, "sounds/_6cd.wav");
        assetFiles.Add(67, "sounds/_6e.wav");
        assetFiles.Add(68, "sounds/_6dd.wav");
        assetFiles.Add(69, "sounds/_6f.wav");
        assetFiles.Add(70, "sounds/_6g.wav");
        assetFiles.Add(71, "sounds/_6fd.wav");
        assetFiles.Add(72, "sounds/_6a.wav");
        assetFiles.Add(73, "sounds/_6gd.wav");
        assetFiles.Add(74, "sounds/_6b.wav");
        assetFiles.Add(75, "sounds/_6ad.wav");
        assetFiles.Add(76, "sounds/_7c.wav");
        assetFiles.Add(77, "sounds/_7d.wav");
        assetFiles.Add(78, "sounds/_7cd.wav");
        assetFiles.Add(79, "sounds/_7e.wav");
        assetFiles.Add(80, "sounds/_7dd.wav");
        assetFiles.Add(81, "sounds/_7f.wav");
        assetFiles.Add(82, "sounds/_7g.wav");
        assetFiles.Add(83, "sounds/_7fd.wav");
        assetFiles.Add(84, "sounds/_7a.wav");
        assetFiles.Add(85, "sounds/_7gd.wav");
        assetFiles.Add(86, "sounds/_7b.wav");
        assetFiles.Add(87, "sounds/_7ad.wav");
        assetFiles.Add(88, "sounds/_8c.wav");
        assetFiles.Add(89, "sounds/tick.wav");

        KeyMapping.Add("a/0", 1);
        KeyMapping.Add("an/0", 1);
        KeyMapping.Add("bbb/0", 1);
        KeyMapping.Add("a#/0", 3);
        KeyMapping.Add("bb/0", 3);
        KeyMapping.Add("cbb/1", 3);
        KeyMapping.Add("b/0", 2);
        KeyMapping.Add("bn/0", 2);
        KeyMapping.Add("a##/0", 2);
        KeyMapping.Add("cb/1", 2);

        KeyMapping.Add("c/1", 4);
        KeyMapping.Add("cn/1", 4);
        KeyMapping.Add("b#/0", 4);
        KeyMapping.Add("dbb/1", 4);
        KeyMapping.Add("a###/0", 4);
        KeyMapping.Add("c#/1", 6);
        KeyMapping.Add("b##/0", 6);
        KeyMapping.Add("db/1", 6);
        KeyMapping.Add("d/1", 5);
        KeyMapping.Add("dn/1", 5);
        KeyMapping.Add("c##/1", 5);
        KeyMapping.Add("ebb/1", 5);
        KeyMapping.Add("b###/0", 5);
        KeyMapping.Add("d#/1", 8);
        KeyMapping.Add("eb/1", 8);
        KeyMapping.Add("fbb/1", 8);
        KeyMapping.Add("c###/1", 8);
        KeyMapping.Add("e/1", 7);
        KeyMapping.Add("en/1", 7);
        KeyMapping.Add("d##/1", 7);
        KeyMapping.Add("fb/1", 7);
        KeyMapping.Add("f/1", 9);
        KeyMapping.Add("fn/1", 9);
        KeyMapping.Add("e#/1", 9);
        KeyMapping.Add("gbb/1", 9);
        KeyMapping.Add("d###/1", 9);
        KeyMapping.Add("f#/1", 11);
        KeyMapping.Add("e##/1", 11);
        KeyMapping.Add("gb/1", 11);
        KeyMapping.Add("g/1", 10);
        KeyMapping.Add("gn/1", 10);
        KeyMapping.Add("f##/1", 10);
        KeyMapping.Add("abb/1", 10);
        KeyMapping.Add("e###/1", 10);
        KeyMapping.Add("g#/1", 13);
        KeyMapping.Add("ab/1", 13);
        KeyMapping.Add("f###/1", 13);
        KeyMapping.Add("a/1", 12);
        KeyMapping.Add("an/1", 12);
        KeyMapping.Add("g##/1", 12);
        KeyMapping.Add("bbb/1", 12);
        KeyMapping.Add("a#/1", 15);
        KeyMapping.Add("bb/1", 15);
        KeyMapping.Add("cbb/2", 15);
        KeyMapping.Add("g###/1", 15);
        KeyMapping.Add("b/1", 14);
        KeyMapping.Add("bn/1", 14);
        KeyMapping.Add("a##/1", 14);
        KeyMapping.Add("cb/2", 14);
                   
        KeyMapping.Add("c/2", 16);
        KeyMapping.Add("cn/2", 16);
        KeyMapping.Add("b#/1", 16);
        KeyMapping.Add("dbb/2", 16);
        KeyMapping.Add("a###/1", 16);
        KeyMapping.Add("c#/2", 18);
        KeyMapping.Add("b##/1", 18);
        KeyMapping.Add("db/2", 18);
        KeyMapping.Add("d/2", 17);
        KeyMapping.Add("dn/2", 17);
        KeyMapping.Add("c##/2", 17);
        KeyMapping.Add("ebb/2", 17);
        KeyMapping.Add("b###/1", 17);
        KeyMapping.Add("d#/2", 20);
        KeyMapping.Add("eb/2", 20);
        KeyMapping.Add("fbb/2", 20);
        KeyMapping.Add("c###/2", 20);
        KeyMapping.Add("e/2", 19);
        KeyMapping.Add("en/2", 19);
        KeyMapping.Add("d##/2", 19);
        KeyMapping.Add("fb/2", 19);
        KeyMapping.Add("f/2", 21);
        KeyMapping.Add("fn/2", 21);
        KeyMapping.Add("e#/2", 21);
        KeyMapping.Add("gbb/2", 21);
        KeyMapping.Add("d###/2", 21);
        KeyMapping.Add("f#/2", 23);
        KeyMapping.Add("e##/2", 23);
        KeyMapping.Add("gb/2", 23);
        KeyMapping.Add("g/2", 22);
        KeyMapping.Add("gn/2", 22);
        KeyMapping.Add("f##/2", 22);
        KeyMapping.Add("abb/2", 22);
        KeyMapping.Add("e###/2", 22);
        KeyMapping.Add("g#/2", 25);
        KeyMapping.Add("ab/2", 25);
        KeyMapping.Add("f###/2", 25);
        KeyMapping.Add("a/2", 24);
        KeyMapping.Add("an/2", 24);
        KeyMapping.Add("g##/2", 24);
        KeyMapping.Add("bbb/2", 24);
        KeyMapping.Add("a#/2", 27);
        KeyMapping.Add("bb/2", 27);
        KeyMapping.Add("cbb/3", 27);
        KeyMapping.Add("g###/2", 27);
        KeyMapping.Add("b/2", 26);
        KeyMapping.Add("bn/2", 26);
        KeyMapping.Add("a##/2", 26);
        KeyMapping.Add("cb/3", 26);
                   
        KeyMapping.Add("c/3", 28);
        KeyMapping.Add("cn/3", 28);
        KeyMapping.Add("b#/2", 28);
        KeyMapping.Add("dbb/3", 28);
        KeyMapping.Add("a###/2", 28);
        KeyMapping.Add("c#/3", 30);
        KeyMapping.Add("b##/2", 30);
        KeyMapping.Add("db/3", 30);
        KeyMapping.Add("d/3", 29);
        KeyMapping.Add("dn/3", 29);
        KeyMapping.Add("c##/3", 29);
        KeyMapping.Add("ebb/3", 29);
        KeyMapping.Add("b###/2", 29);
        KeyMapping.Add("d#/3", 32);
        KeyMapping.Add("eb/3", 32);
        KeyMapping.Add("fbb/3", 32);
        KeyMapping.Add("c###/3", 32);
        KeyMapping.Add("e/3", 31);
        KeyMapping.Add("en/3", 31);
        KeyMapping.Add("d##/3", 31);
        KeyMapping.Add("fb/3", 31);
        KeyMapping.Add("f/3", 33);
        KeyMapping.Add("fn/3", 33);
        KeyMapping.Add("e#/3", 33);
        KeyMapping.Add("gbb/3", 33);
        KeyMapping.Add("d###/3", 33);
        KeyMapping.Add("f#/3", 35);
        KeyMapping.Add("e##/3", 35);
        KeyMapping.Add("gb/3", 35);
        KeyMapping.Add("g/3", 34);
        KeyMapping.Add("gn/3", 34);
        KeyMapping.Add("f##/3", 34);
        KeyMapping.Add("abb/3", 34);
        KeyMapping.Add("e###/3", 34);
        KeyMapping.Add("g#/3", 37);
        KeyMapping.Add("ab/3", 37);
        KeyMapping.Add("f###/3", 37);
        KeyMapping.Add("a/3", 36);
        KeyMapping.Add("an/3", 36);
        KeyMapping.Add("g##/3", 36);
        KeyMapping.Add("bbb/3", 36);
        KeyMapping.Add("a#/3", 39);
        KeyMapping.Add("bb/3", 39);
        KeyMapping.Add("cbb/4", 39);
        KeyMapping.Add("g###/3", 39);
        KeyMapping.Add("b/3", 38);
        KeyMapping.Add("bn/3", 38);
        KeyMapping.Add("a##/3", 38);
        KeyMapping.Add("cb/4", 38);
                   
        KeyMapping.Add("c/4", 40);
        KeyMapping.Add("cn/4", 40);
        KeyMapping.Add("b#/3", 40);
        KeyMapping.Add("dbb/4", 40);
        KeyMapping.Add("a###/3", 40);
        KeyMapping.Add("c#/4", 42);
        KeyMapping.Add("b##/3", 42);
        KeyMapping.Add("db/4", 42);
        KeyMapping.Add("d/4", 41);
        KeyMapping.Add("dn/4", 41);
        KeyMapping.Add("c##/4", 41);
        KeyMapping.Add("ebb/4", 41);
        KeyMapping.Add("b###/3", 41);
        KeyMapping.Add("d#/4", 44);
        KeyMapping.Add("eb/4", 44);
        KeyMapping.Add("fbb/4", 44);
        KeyMapping.Add("c###/4", 44);
        KeyMapping.Add("e/4", 43);
        KeyMapping.Add("en/4", 43);
        KeyMapping.Add("d##/4", 43);
        KeyMapping.Add("fb/4", 43);
        KeyMapping.Add("f/4", 45);
        KeyMapping.Add("fn/4", 45);
        KeyMapping.Add("e#/4", 45);
        KeyMapping.Add("gbb/4", 45);
        KeyMapping.Add("d###/4", 45);
        KeyMapping.Add("f#/4", 47);
        KeyMapping.Add("e##/4", 47);
        KeyMapping.Add("gb/4", 47);
        KeyMapping.Add("g/4", 46);
        KeyMapping.Add("gn/4", 46);
        KeyMapping.Add("f##/4", 46);
        KeyMapping.Add("abb/4", 46);
        KeyMapping.Add("e###/4", 46);
        KeyMapping.Add("g#/4", 49);
        KeyMapping.Add("ab/4", 49);
        KeyMapping.Add("f###/4", 49);
        KeyMapping.Add("a/4", 48);
        KeyMapping.Add("an/4", 48);
        KeyMapping.Add("g##/4", 48);
        KeyMapping.Add("bbb/4", 48);
        KeyMapping.Add("a#/4", 51);
        KeyMapping.Add("bb/4", 51);
        KeyMapping.Add("cbb/5", 51);
        KeyMapping.Add("g###/4", 51);
        KeyMapping.Add("b/4", 50);
        KeyMapping.Add("bn/4", 50);
        KeyMapping.Add("a##/4", 50);
        KeyMapping.Add("cb/5", 50);
                   
        KeyMapping.Add("c/5", 52);
        KeyMapping.Add("cn/5", 52);
        KeyMapping.Add("b#/4", 52);
        KeyMapping.Add("dbb/5", 52);
        KeyMapping.Add("a###/4", 52);
        KeyMapping.Add("c#/5", 54);
        KeyMapping.Add("b##/4", 54);
        KeyMapping.Add("db/5", 54);
        KeyMapping.Add("d/5", 53);
        KeyMapping.Add("dn/5", 53);
        KeyMapping.Add("c##/5", 53);
        KeyMapping.Add("ebb/5", 53);
        KeyMapping.Add("b###/4", 53);
        KeyMapping.Add("d#/5", 56);
        KeyMapping.Add("eb/5", 56);
        KeyMapping.Add("fbb/5", 56);
        KeyMapping.Add("c###/5", 56);
        KeyMapping.Add("e/5", 55);
        KeyMapping.Add("en/5", 55);
        KeyMapping.Add("d##/5", 55);
        KeyMapping.Add("fb/5", 55);
        KeyMapping.Add("f/5", 57);
        KeyMapping.Add("fn/5", 57);
        KeyMapping.Add("e#/5", 57);
        KeyMapping.Add("gbb/5", 57);
        KeyMapping.Add("d###/5", 57);
        KeyMapping.Add("f#/5", 59);
        KeyMapping.Add("e##/5", 59);
        KeyMapping.Add("gb/5", 59);
        KeyMapping.Add("g/5", 58);
        KeyMapping.Add("gn/5", 58);
        KeyMapping.Add("f##/5", 58);
        KeyMapping.Add("abb/5", 58);
        KeyMapping.Add("e###/5", 58);
        KeyMapping.Add("g#/5", 61);
        KeyMapping.Add("ab/5", 61);
        KeyMapping.Add("f###/5", 61);
        KeyMapping.Add("a/5", 60);
        KeyMapping.Add("an/5", 60);
        KeyMapping.Add("g##/5", 60);
        KeyMapping.Add("bbb/5", 60);
        KeyMapping.Add("a#/5", 63);
        KeyMapping.Add("bb/5", 63);
        KeyMapping.Add("cbb/6", 63);
        KeyMapping.Add("g###/5", 63);
        KeyMapping.Add("b/5", 62);
        KeyMapping.Add("bn/5", 62);
        KeyMapping.Add("a##/5", 62);
        KeyMapping.Add("cb/6", 62);
                   
        KeyMapping.Add("c/6", 52 + 12);
        KeyMapping.Add("cn/6", 52 + 12);
        KeyMapping.Add("b#/5", 52 + 12);
        KeyMapping.Add("dbb/6", 52 + 12);
        KeyMapping.Add("a###/5", 52 + 12);
        KeyMapping.Add("c#/6", 54 + 12);
        KeyMapping.Add("b##/5", 54 + 12);
        KeyMapping.Add("db/6", 54 + 12);
        KeyMapping.Add("d/6", 53 + 12);
        KeyMapping.Add("dn/6", 53 + 12);
        KeyMapping.Add("c##/6", 53 + 12);
        KeyMapping.Add("ebb/6", 53 + 12);
        KeyMapping.Add("b###/5", 53 + 12);
        KeyMapping.Add("d#/6", 56 + 12);
        KeyMapping.Add("eb/6", 56 + 12);
        KeyMapping.Add("fbb/6", 56 + 12);
        KeyMapping.Add("c###/6", 56 + 12);
        KeyMapping.Add("e/6", 55 + 12);
        KeyMapping.Add("en/6", 55 + 12);
        KeyMapping.Add("d##/6", 55 + 12);
        KeyMapping.Add("fb/6", 55 + 12);
        KeyMapping.Add("f/6", 57 + 12);
        KeyMapping.Add("fn/6", 57 + 12);
        KeyMapping.Add("e#/6", 57 + 12);
        KeyMapping.Add("gbb/6", 57 + 12);
        KeyMapping.Add("d###/6", 57 + 12);
        KeyMapping.Add("f#/6", 59 + 12);
        KeyMapping.Add("e##/6", 59 + 12);
        KeyMapping.Add("gb/6", 59 + 12);
        KeyMapping.Add("g/6", 58 + 12);
        KeyMapping.Add("gn/6", 58 + 12);
        KeyMapping.Add("f##/6", 58 + 12);
        KeyMapping.Add("abb/6", 58 + 12);
        KeyMapping.Add("e###/6", 58 + 12);
        KeyMapping.Add("g#/6", 61 + 12);
        KeyMapping.Add("ab/6", 61 + 12);
        KeyMapping.Add("f###/6", 61 + 12);
        KeyMapping.Add("a/6", 60 + 12);
        KeyMapping.Add("an/6", 60 + 12);
        KeyMapping.Add("g##/6", 60 + 12);
        KeyMapping.Add("bbb/6", 60 + 12);
        KeyMapping.Add("a#/6", 63 + 12);
        KeyMapping.Add("bb/6", 63 + 12);
        KeyMapping.Add("cbb/7", 63 + 12);
        KeyMapping.Add("g###/6", 63 + 12);
        KeyMapping.Add("b/6", 62 + 12);
        KeyMapping.Add("bn/6", 62 + 12);
        KeyMapping.Add("a##/6", 62 + 12);
        KeyMapping.Add("cb/7", 62 + 12);
                   
        KeyMapping.Add("c/7", 52 + 12 + 12);
        KeyMapping.Add("cn/7", 52 + 12 + 12);
        KeyMapping.Add("b#/6", 52 + 12 + 12);
        KeyMapping.Add("dbb/7", 52 + 12 + 12);
        KeyMapping.Add("a###/6", 52 + 12 + 12);
        KeyMapping.Add("c#/7", 54 + 12 + 12);
        KeyMapping.Add("b##/6", 54 + 12 + 12);
        KeyMapping.Add("db/7", 54 + 12 + 12);
        KeyMapping.Add("d/7", 53 + 12 + 12);
        KeyMapping.Add("dn/7", 53 + 12 + 12);
        KeyMapping.Add("c##/7", 53 + 12 + 12);
        KeyMapping.Add("ebb/7", 53 + 12 + 12);
        KeyMapping.Add("b###/6", 53 + 12 + 12);
        KeyMapping.Add("d#/7", 56 + 12 + 12);
        KeyMapping.Add("eb/7", 56 + 12 + 12);
        KeyMapping.Add("fbb/7", 56 + 12 + 12);
        KeyMapping.Add("c###/7", 56 + 12 + 12);
        KeyMapping.Add("e/7", 55 + 12 + 12);
        KeyMapping.Add("en/7", 55 + 12 + 12);
        KeyMapping.Add("d##/7", 55 + 12 + 12);
        KeyMapping.Add("fb/7", 55 + 12 + 12);
        KeyMapping.Add("f/7", 57 + 12 + 12);
        KeyMapping.Add("fn/7", 57 + 12 + 12);
        KeyMapping.Add("e#/7", 57 + 12 + 12);
        KeyMapping.Add("gbb/7", 57 + 12 + 12);
        KeyMapping.Add("d###/7", 57 + 12 + 12);
        KeyMapping.Add("f#/7", 59 + 12 + 12);
        KeyMapping.Add("e##/7", 59 + 12 + 12);
        KeyMapping.Add("gb/7", 59 + 12 + 12);
        KeyMapping.Add("g/7", 58 + 12 + 12);
        KeyMapping.Add("gn/7", 58 + 12 + 12);
        KeyMapping.Add("f##/7", 58 + 12 + 12);
        KeyMapping.Add("abb/7", 58 + 12 + 12);
        KeyMapping.Add("e###/7", 58 + 12 + 12);
        KeyMapping.Add("g#/7", 61 + 12 + 12);
        KeyMapping.Add("ab/7", 61 + 12 + 12);
        KeyMapping.Add("f###/7", 61 + 12 + 12);
        KeyMapping.Add("a/7", 60 + 12 + 12);
        KeyMapping.Add("an/7", 60 + 12 + 12);
        KeyMapping.Add("g##/7", 60 + 12 + 12);
        KeyMapping.Add("bbb/7", 60 + 12 + 12);
        KeyMapping.Add("a#/7", 63 + 12 + 12);
        KeyMapping.Add("bb/7", 63 + 12 + 12);
        KeyMapping.Add("cbb/8", 63 + 12 + 12);
        KeyMapping.Add("g###/7", 63 + 12 + 12);
        KeyMapping.Add("b/7", 62 + 12 + 12);
        KeyMapping.Add("bn/7", 62 + 12 + 12);
        KeyMapping.Add("a##/7", 62 + 12 + 12);
        KeyMapping.Add("cb/8", 62 + 12 + 12);
                   
        KeyMapping.Add("c/8", 88);
        KeyMapping.Add("cn/8", 88);
        KeyMapping.Add("b#/7", 88);
        KeyMapping.Add("dbb/8", 88);
        KeyMapping.Add("a###/7", 88);

        KeyMapping.Add("tick", 89);
    }




    static FM_SoundPool mInstance;
    public static FM_SoundPool GetInstance()
    {
        if(mInstance == null)
            mInstance = new FM_SoundPool();
        return mInstance;
    }

    public FM_SoundPool()
    {
        InitMap();

        threadMap = new Dictionary<int,PlayThread>();
        //AudioAttributes attributes = new AudioAttributes.Builder()
        //    .SetUsage(AudioUsageKind.Game)
        //    .SetContentType(AudioContentType.Sonification)
        //    .Build();
        //sndPool = new SoundPool()
        //    .SetAudioAttributes(attributes)
        //    .SetMaxStreams(16)
        //    .Build();

        //AtomicInteger loaded_count = new AtomicInteger();
        //sndPool.SetOnLoadCompleteListener(new MyOnLoadCompleteListener(loaded_count));

        FM_ScorePlayer.GetInstance().SoundsLoaded = 0;

        soundMap = new Dictionary<int, AudioClip>();
        //for (int i = 1; i <= 89; i++)
        foreach (var asset in assetFiles)
        {
            try
            {
                string filePath = asset.Value.Substring(0,asset.Value.LastIndexOf('.'));
                soundMap.Add(asset.Key, Resources.Load<AudioClip>(filePath));
            }
            catch (IOException ignored)
            {
                Debug.LogError(ignored.Message);
            }
            //Thread.Sleep(20);
        }
        FM_ScorePlayer.GetInstance().SoundsLoaded = assetFiles.Count;
    }

    public void PlayKey(MonoBehaviour mono, int key)
    {
        PlayKey(mono, key, false, false);
    }

    public void PlayKey(MonoBehaviour mono, int key, bool nextPause)
    {
        PlayKey(mono, key, nextPause, false);
    }

    public void PlaySilentKey(MonoBehaviour mono, int key)
    {
        PlayKey(mono, key, false, true);
    }

    int index = -1;

    public void PlayKey(MonoBehaviour mono,int key, bool nextPause, bool silent)
    {
        if (key == -1) return;
        if (IsKeyNotPlaying(key))
        {
            if (index >= 0 && index < FM_Helper.testMap.Count)
            {
                FM_Helper.TestKeyNotes t = FM_Helper.testMap[index];
                if (t != null)
                {
                    FM_Note note = t.note;
                    //Log.d(TAG, "playKey: " + key + "  note:" + note.note + "  oct" + note.octave + " acc" + note.accidental + "  index:" + index);
                }
            }
            index++;

            PlayThread thread = new PlayThread(key, nextPause, silent);

            thread.Start(mono);
            threadMap.Add(key, thread);
        }
    }

    public void StopKey(int key)
    {
        if (key == -1) return;
        try
        {
            threadMap.TryGetValue(key,out PlayThread thread);
            if (thread != null)
            {
                thread.Stop();
                threadMap.Remove(key);
            }
        }
        catch (Exception ignored)
        {
            Debug.LogError(ignored.Message);
        }
    }

    public bool IsKeyNotPlaying(int key)
    {
        threadMap.TryGetValue(key, out PlayThread thread);
        return thread == null;
    }

    public static string TranslateKey(string key)
    {
        key = key.Replace("do", "c");
        key = key.Replace("re", "d");
        key = key.Replace("mi", "e");
        key = key.Replace("fa", "f");
        key = key.Replace("sol", "g");
        key = key.Replace("la", "a");
        key = key.Replace("si", "b");
        return key;
    }

    public static int GetIndex(string key)
    {
        key = TranslateKey(key.ToLower());
        if (key.StartsWith("r"))
            return -1;
        key = key.Replace("n", "");
        KeyMapping.TryGetValue(key, out int result);

        return result;// ?? 1;
    }

    public static int GetDurationInMs(FM_DurationValue duration, int tupletSize, int tempo, FM_TimeSignatureValue timeSignatureD)
    {
        if (tempo == 0) tempo = 60;
        if (timeSignatureD == 0) timeSignatureD = TIME_SIGNATURE_D;
        if (timeSignatureD == 0) timeSignatureD = FM_TimeSignatureValue._4;
        var timeSignatureN = TIME_SIGNATURE_N;
        if (timeSignatureN == 0) timeSignatureN = FM_TimeSignatureValue._4;
        int multiply = 1;       //simple meter (2, 3, and 4)
        if (timeSignatureN == FM_TimeSignatureValue._6 || timeSignatureN == FM_TimeSignatureValue._9 || timeSignatureN == FM_TimeSignatureValue._12)
            multiply = 3; //compound meter

        float d = (60.0f / (tempo * multiply)) * ((int)timeSignatureD / 4f) * 4000.0f;
        //if (timeSignatureD == 8) d = d * (4f/3f);

        if ((int)duration > 50)
        {
            d = d * 1.5f;
            duration = duration - 50;
        }
        if (tupletSize == 3) d = d * 2f / 3.0f;
        if (tupletSize == 5) d = d * 4f / 5.0f;
        if (tupletSize == 6) d = d * 4f / 6.0f;
        if (tupletSize == 2) d = d * 3f / 4.0f;
        if (tupletSize == 4) d = d * 3f / 4.0f;

        if (duration == FM_DurationValue.NOTE_WHOLE) return (int)(d * 1);
        if (duration == FM_DurationValue.NOTE_HALF) return (int)(d * 1 / 2f);
        if (duration == FM_DurationValue.NOTE_QUARTER) return (int)(d * 1 / 4f);
        if (duration == FM_DurationValue.NOTE_EIGHTH) return (int)(d * 1 / 8f);
        if (duration == FM_DurationValue.NOTE_SIXTEENTH) return (int)(d * 1 / 16f);
        if (duration == FM_DurationValue.NOTE_THIRTY_SECOND) return (int)(d * 1 / 32f);
        return 0;
    }

    public static float CustomDelay(long duration, bool coolDown)
    {
        long current = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
        long start = current;
        long end = current + duration * 1000000;
        while (current < end)
        {
            current = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
            if ((!coolDown) && (!playing)) return duration;
        }
        return (current - start) / 1000000f;
    }

    public void StopAllSound()
    {
        playing = false;
         foreach(var thread in threadMap.Values)
        {
            if (thread != null)
            {
                thread.Stop();
            }
        }
        threadMap.Clear();
    }
}